First look at the seven variables I chose in terms of box-and-jitter plots:

Autocorrelation function:

library(pastecs)
corrFiles <- list.files("/Volumes/PhD/BijelData/Batch_II_full_resolution_ED_images/Autocorrelation", pattern=".txt", full.names = TRUE)
corrFileNames <- list.files("/Volumes/PhD/BijelData/Batch_II_full_resolution_ED_images/Autocorrelation", pattern=".txt")
autoCorr <- do.call(cbind, lapply(corrFiles, read.csv, header=FALSE))
colnames(autoCorr) <- corrFileNames
exp_Data <- read.csv("/Volumes/PhD/BijelData/Bijel_Data_Cleaner_ToRead.csv", na.strings = "?")
exp_Data$Sample.Number <- as.character(exp_Data$Sample.Number)
corrFileID <- sapply(strsplit(corrFileNames,"_"), `[`,1) #`[` is a function that takes the subset of x, the input to this function is x (strsplit...) and the element of x that I want, ie the 1st one
colnames(autoCorr) <- corrFileID
rownames(exp_Data) <- exp_Data$Sample.Number
autoCorr_transpose <- data.frame(t(autoCorr))
exp_Data$Autocorrelation <- autoCorr_transpose[match(row.names(exp_Data),row.names(autoCorr_transpose)),c(1:256)]
turningPoints <- lapply(1:135, function(y) turnpoints(unlist(exp_Data$Autocorrelation[y,])))
exp_Data$Auto.Turning.Points <- turningPoints
firstTurn <- lapply(1:135, function(y) exp_Data$Auto.Turning.Points[[y]]$tppos[[1]])
exp_Data$Auto.First.Turn <- unlist(firstTurn)
peakDepth <- lapply(1:135, function(y) exp_Data$Autocorrelation[[y,exp_Data$Auto.Turning.Points[[y]]$tppos[[2]]]]-exp_Data$Autocorrelation[[y,exp_Data$Auto.Turning.Points[[y]]$tppos[[1]]]])
exp_Data$Auto.Peak.Depth <- unlist(peakDepth)
exp_Data$Auto.Num.Turns <- unlist(lapply(1:135, function(y) length(exp_Data$Auto.Turning.Points[[y]]$tppos)))

Radial profile:

radFiles <- list.files("/Volumes/PhD/BijelData/Batch_II_full_resolution_ED_images/Profiles", pattern=".txt", full.names = TRUE)
radFileNames <- list.files("/Volumes/PhD/BijelData/Batch_II_full_resolution_ED_images/Profiles", pattern=".txt")
radProf_data <- do.call(cbind, lapply(radFiles, read.csv, header=FALSE))
colnames(radProf_data) <- radFileNames
radFileID <- sapply(strsplit(radFileNames,"_"), `[`,1) #`[` is a function that takes the subset of x, the input to this function is x (strsplit...) and the element of x that I want, ie the 1st one
colnames(radProf_data) <- radFileID
rownames(exp_Data) <- exp_Data$Sample.Number
radProf_data_transpose <- data.frame(t(radProf_data))
exp_Data$Radial.Profile <- radProf_data_transpose[match(row.names(exp_Data),row.names(radProf_data_transpose)),c(1:256)]
r <- c(1:256)
y <- exp_Data$Radial.Profile[2:20]
lineFits <- lapply(1:135, function(n) lm(unlist(y[n,]) ~ r[2:20]))
lineCoeffs <- lapply(lineFits, function(m) m$coefficients)
lineGradients <- lapply (1:135, function(p) unname(lineCoeffs[[p]][2]))
exp_Data$Rad.Line.Gradients <- unlist(lineGradients)
avVal <- lapply(1:135, function(n) mean(unlist(exp_Data$Radial.Profile[n,29:31])))
exp_Data$Rad.Val.30 <- unlist(avVal)
jumps <- function(y,n){
  a <- exp_Data$Radial.Profile[[y,n+5]]
  b <- exp_Data$Radial.Profile[[y,n]]
  out <- a-b
  return(out)
}
#nMax <- lapply(1:135, function(y) length(exp_Data$Radial.Profile[[y]])-1)
jumpSizes <- lapply(1:135, function(y) mapply(jumps, y, 1:251))
maxJump <- lapply(1:135, function(y) max(jumpSizes[[y]]))
exp_Data$Max.Rad.Jump <- unlist(maxJump)
maxJumpX <- lapply(1:135, function(y) which.max(jumpSizes[[y]]))
exp_Data$Max.Rad.Jump.X <- unlist(maxJumpX)

Plot them all.

library(ggplot2)
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_auto-num-turns.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Auto.Num.Turns, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +  xlab("Bijel?") + ylab("Number of turning points in ACF")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_auto-peak-depth.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Auto.Peak.Depth, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +  xlab("Bijel?") + ylab("Depth of first ACF trough/peak pair")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_auto-first-turn.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Auto.First.Turn, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +
  xlab("Bijel?") + ylab("Position of first ACF turning point")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_rad-val-30.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Rad.Val.30, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +  xlab("Bijel?") + ylab("Average of points 29-31 of SF")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_rad-line-gradients.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Rad.Line.Gradients, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +  xlab("Bijel?") + ylab("Gradient of first 20 points of SF")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_rad-max-jump.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Max.Rad.Jump, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +  xlab("Bijel?") + ylab("Height of biggest positive jump in SF")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 
png(file='/Users/s1101153/Desktop/bijels1_graphs/bjp_rad-max-jump-x.png', width=600, height=400) 
ggplot(exp_Data, aes(x=as.factor(Bijel), y=Max.Rad.Jump.X, fill=Bijel)) + geom_boxplot(alpha=0.3) + geom_jitter(alpha=0.5) +  xlab("Bijel?") + ylab("Position (in r) of biggest jump in SF")+theme(legend.position="none", text = element_text(size=24), axis.title = element_text(size=21))
dev.off()
null device 
          1 

Plot examples of the ACF and SF too:

x=c(1:255)
xSF=x*640.17/512
L <- 640.17
pixel_to_micron <- 2*pi/L
#example SF plots (liquid channel)
bijelSF_l <- unlist(read.csv("/Volumes/PhD/BijelData/SF_bothChannels/52ii_Image23.tif_radProf_channel0.txt"))
noBijelSF_l <- unlist(read.csv("/Volumes/PhD/BijelData/SF_bothChannels/54i_Image9.tif_radProf_channel0.txt"))
png('~/Desktop/bijels1_graphs/SFexamples_liq.png', res=300, width=1800, height=1200)
plot(xSF, bijelSF_l, type="l", lwd=2, col="red", xlab = "q (1/μm)", ylab="Structure Factor", ylim=c(0.4,2.2),cex.axis=1.5,cex.lab=2,mgp=c(2.2,0.7,0))
lines(xSF, noBijelSF_l, lwd=2)
legend("topright", legend = c("Bijel", "Non-bijel"), col=c("red", "black"), lwd=2)#, cex=1.4)
dev.off()
null device 
          1 
#example ACF plots (liquid channel)
bijelACF_l <- unlist(read.csv("/Volumes/PhD/BijelData/LiquidChannel/autoCorr/52ii_Image23.tif_autoCorr_channel0.txt"))
noBijelACF_l <- unlist(read.csv("/Volumes/PhD/BijelData/LiquidChannel/autoCorr/54i_Image9.tif_autoCorr_channel0.txt"))
x=c(1:255)
xACF=x*pixel_to_micron
png('~/Desktop/bijels1_graphs/ACFexamples_liq.png', res=300, width=1800, height=1200)
plot(xACF, bijelACF_l, type="l", lwd=2, col="red", xlab = "r (μm)", ylab="Autocorrelation Function",cex.axis=1.5,cex.lab=2,mgp=c(2.2,0.7,0))
lines(xACF, noBijelACF_l, lwd=2)
legend("topright", legend = c("Bijel", "Non-bijel"), col=c("red", "black"), lwd=2)
dev.off()
null device 
          1 

Now scale the data for accurate results:

attach(exp_Data)
The following object is masked from nbDat (pos = 3):

    Bijel

The following object is masked from bDat (pos = 4):

    Bijel

The following object is masked from bDat (pos = 5):

    Bijel

The following object is masked from nbDat (pos = 6):

    Bijel

The following object is masked from bDat (pos = 7):

    Bijel

The following objects are masked from exp_Data (pos = 8):

    Ammonia..g, Bijel, Bubbles, Date.Made, Droplets, Ethanediol, Excess.Particles, HMDS..g, HMDS.Ratio, Nitromethane,
    Nitromethane.mass.fraction, Particle.Batch, Particle.Volume.Fraction..., Particles, Quench.Method, Sample.Number

The following objects are masked from exp_Data (pos = 9):

    Ammonia..g, Bijel, Bubbles, Date.Made, Droplets, Ethanediol, Excess.Particles, HMDS..g, HMDS.Ratio, Nitromethane,
    Nitromethane.mass.fraction, Particle.Batch, Particle.Volume.Fraction..., Particles, Quench.Method, Sample.Number

The following object is masked from nbDat (pos = 10):

    Bijel

The following object is masked from bDat (pos = 11):

    Bijel

The following object is masked from nbDat (pos = 12):

    Bijel

The following object is masked from bDat (pos = 13):

    Bijel

The following objects are masked from exp_Data (pos = 14):

    Ammonia..g, Bijel, Bubbles, Date.Made, Droplets, Ethanediol, Excess.Particles, HMDS..g, HMDS.Ratio, Nitromethane,
    Nitromethane.mass.fraction, Particle.Batch, Particle.Volume.Fraction..., Particles, Quench.Method, Sample.Number

The following objects are masked from exp_Data (pos = 15):

    Ammonia..g, Bijel, Bubbles, Date.Made, Droplets, Ethanediol, Excess.Particles, HMDS..g, HMDS.Ratio, Nitromethane,
    Nitromethane.mass.fraction, Particle.Batch, Particle.Volume.Fraction..., Particles, Quench.Method, Sample.Number

The following object is masked from nbDat (pos = 16):

    Bijel

The following object is masked from bDat (pos = 17):

    Bijel

The following objects are masked from exp_Data (pos = 18):

    Ammonia..g, Bijel, Bubbles, Date.Made, Droplets, Ethanediol, Excess.Particles, HMDS..g, HMDS.Ratio, Nitromethane,
    Nitromethane.mass.fraction, Particle.Batch, Particle.Volume.Fraction..., Particles, Quench.Method, Sample.Number

The following objects are masked from nbDat (pos = 19):

    Auto.First.Turn, Bijel

The following objects are masked from bDat (pos = 20):

    Auto.First.Turn, Bijel

The following objects are masked from nbDat (pos = 21):

    Auto.First.Turn, Bijel

The following objects are masked from bDat (pos = 22):

    Auto.First.Turn, Bijel

The following object is masked from nbDat (pos = 23):

    Bijel

The following object is masked from bDat (pos = 24):

    Bijel

The following object is masked from nbDat (pos = 25):

    Bijel

The following object is masked from bDat (pos = 26):

    Bijel

The following objects are masked from exp_Data (pos = 32):

    Ammonia..g, Auto.First.Turn, Auto.Num.Turns, Auto.Peak.Depth, Auto.Turning.Points, Autocorrelation, Bijel, Bubbles,
    Date.Made, Droplets, Ethanediol, Excess.Particles, HMDS..g, HMDS.Ratio, Max.Rad.Jump, Max.Rad.Jump.X, Nitromethane,
    Nitromethane.mass.fraction, Particle.Batch, Particle.Volume.Fraction..., Particles, Quench.Method,
    Rad.Line.Gradients, Rad.Val.30, Radial.Profile, Sample.Number
dat=data.frame(Auto.Num.Turns, Auto.Peak.Depth, Rad.Val.30, Rad.Line.Gradients, Max.Rad.Jump, Max.Rad.Jump.X, Auto.First.Turn)
means = unlist(lapply(1:7, function(n) mean(unlist(dat[n][]))))
datScaled = data.frame(lapply(1:7, function(n) dat[n][]*means[1]/means[n]))
datScaled$Bijel = Bijel
head(datScaled)

Now run decision tree (ignore this for now…)

library(caret)
library(rpart.plot)
set.seed(1234)
trCtrl <- trainControl(method="repeatedcv", number=10, repeats = 3)
dtreeFit <- train(Bijel ~., data=datScaled, method="rpart", parms=list(split="gini"), trControl=trCtrl, tuneLength=10)
dtreeFit
CART 

135 samples
  7 predictor
  2 classes: 'n', 'y' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 122, 122, 122, 121, 122, 121, ... 
Resampling results across tuning parameters:

  cp          Accuracy   Kappa    
  0.00000000  0.7912210  0.5187700
  0.05426357  0.8372405  0.5757128
  0.10852713  0.8372405  0.5757128
  0.16279070  0.8372405  0.5757128
  0.21705426  0.8372405  0.5757128
  0.27131783  0.8372405  0.5757128
  0.32558140  0.8372405  0.5757128
  0.37984496  0.8372405  0.5757128
  0.43410853  0.8372405  0.5757128
  0.48837209  0.7065812  0.1199017

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was cp = 0.4341085.
prp(dtreeFit$finalModel, box.palette = "Reds", tweak=1.2, extra=101)

# table(Predict=dtreeFit$pred(datScaled), true=Bijel)

Now knn:

#knn - all 7 variables
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled, method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2149573
knn_fit
k-Nearest Neighbors 

135 samples
  7 predictor
  2 classes: 'n', 'y' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 122, 122, 122, 121, 122, 121, ... 
Resampling results across tuning parameters:

  k   Accuracy   Kappa     
   5  0.7305739  0.35631464
   7  0.7303907  0.34922530
   9  0.7329792  0.35084850
  11  0.7628083  0.40815297
  13  0.7720147  0.42145392
  15  0.7771429  0.44347609
  17  0.7778266  0.45160919
  19  0.7753114  0.44145188
  21  0.7725641  0.45235690
  23  0.7554823  0.41454809
  25  0.7510867  0.41227897
  27  0.7487057  0.41254685
  29  0.7611600  0.45148930
  31  0.7611600  0.45148930
  33  0.7611600  0.45148930
  35  0.7637241  0.45555376
  37  0.7637241  0.45555376
  39  0.7611600  0.44818781
  41  0.7609768  0.44373489
  43  0.7582295  0.43255068
  45  0.7534676  0.42317590
  47  0.7534676  0.42058487
  49  0.7556899  0.42414218
  51  0.7556899  0.42246529
  53  0.7630403  0.43007752
  55  0.7850427  0.46611045
  57  0.7706838  0.41692672
  59  0.7661050  0.36673823
  61  0.7263126  0.18689266
  63  0.6891331  0.03281686
  65  0.6819902  0.00000000
  67  0.6819902  0.00000000
  69  0.6819902  0.00000000
  71  0.6819902  0.00000000
  73  0.6819902  0.00000000
  75  0.6819902  0.00000000
  77  0.6819902  0.00000000
  79  0.6819902  0.00000000
  81  0.6819902  0.00000000
  83  0.6819902  0.00000000
  85  0.6819902  0.00000000
  87  0.6819902  0.00000000

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was k = 55.
plot(knn_fit)

pairs(datScaled[-8], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled)
pairs(datScaled[-8], col=bijelPred, main="Predicted")

Try removing variables

#Auto.Num.Turns removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-1], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2149817
plot(knn_fit)

pairs(datScaled[2:7], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-1])
pairs(datScaled[2:7], col=bijelPred, main="Predicted")

#Auto.Peak.Depth removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-2], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2146398
plot(knn_fit)

pairs(datScaled[c(1,3:7)], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-2])
pairs(datScaled[c(1,3:7)], col=bijelPred, main="Predicted")

#Rad.Val.30 removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-3], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2149573
plot(knn_fit)

pairs(datScaled[c(1:2,4:7)], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-3])
pairs(datScaled[c(1:2,4:7)], col=bijelPred, main="Predicted")

#Rad.Line.Gradients removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-4], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2175214
plot(knn_fit)

pairs(datScaled[c(1:3,5:7)], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-4])
pairs(datScaled[c(1:3,5:7)], col=bijelPred, main="Predicted")

#Max.Rad.Jump removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-5], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2117338
plot(knn_fit)

pairs(datScaled[c(1:4,6:7)], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-5])
pairs(datScaled[c(1:4,6:7)], col=bijelPred, main="Predicted")

#Max.Rad.Jump.X removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-6], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.1900488
plot(knn_fit)

pairs(datScaled[c(1:5,7)], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-6])
pairs(datScaled[c(1:5,7)], col=bijelPred, main="Predicted")

#Auto.First.Turns removed
set.seed(1234)
knn_fit <- train(Bijel ~., data=datScaled[-7], method="knn", trControl=trCtrl, tuneLength=42)
error=1-knn_fit$results[row.names(knn_fit$bestTune),]$Accuracy
error
[1] 0.2439683
plot(knn_fit)

pairs(datScaled[c(1:7)], col=Bijel, main = "Data")

bijelPred <- predict(knn_fit, datScaled[-7])
pairs(datScaled[c(1:7)], col=bijelPred, main="Predicted")

Now remove all the variables whose removal makes the error lower than the original value of 0.2149573:

datImproved = datScaled[-6]
datImprovedSmall = datImproved[-5]

F-statistic calculations

vars <- names(datImproved)
var_fstat <- sapply(vars, function(x) {
    summary(lm(as.formula(paste(x, " ~ Bijel")), data = datImproved))$fstatistic[1]
})
using type = "numeric" with a factor response will be ignoredthe response appeared on the right-hand side and was droppedproblem with term 1 in model.matrix: no columns are assigned‘-’ not meaningful for factors‘^’ not meaningful for factors
sort(unlist(var_fstat), decreasing=TRUE)
   Auto.First.Turn.value Rad.Line.Gradients.value         Rad.Val.30.value       Max.Rad.Jump.value    Auto.Peak.Depth.value 
             61.57758959              49.81943975              27.27489050              13.70016954              12.21510388 
    Auto.Num.Turns.value 
              0.09194059 
vars <- names(datImprovedSmall)
var_fstat <- sapply(vars, function(x) {
    summary(lm(as.formula(paste(x, " ~ Bijel")), data = datImprovedSmall))$fstatistic[1]
})
using type = "numeric" with a factor response will be ignoredthe response appeared on the right-hand side and was droppedproblem with term 1 in model.matrix: no columns are assigned‘-’ not meaningful for factors‘^’ not meaningful for factors
sort(unlist(var_fstat), decreasing=TRUE)
   Auto.First.Turn.value Rad.Line.Gradients.value         Rad.Val.30.value    Auto.Peak.Depth.value     Auto.Num.Turns.value 
             61.57758959              49.81943975              27.27489050              12.21510388               0.09194059 

stick with removing the two variables, i.e. use datImprovedSmall

Model minimisation

errors <- rep(NA, 5)
set.seed(1234)
knn_fit_turn5 <- train(Bijel ~., data=datImprovedSmall, method="knn", trControl=trCtrl, tuneLength=42)
errors[1]=1-knn_fit_turn5$results[row.names(knn_fit_turn5$bestTune),]$Accuracy
#plot(knn_fit_turn)
set.seed(1234)
knn_fit_turn4 <- train(Bijel ~., data=datImprovedSmall[c("Bijel", "Auto.First.Turn", "Rad.Line.Gradients", "Rad.Val.30", "Auto.Peak.Depth")], method="knn", trControl=trCtrl, tuneLength=42)
errors[2]=1-knn_fit_turn4$results[row.names(knn_fit_turn4$bestTune),]$Accuracy
set.seed(1234)
knn_fit_turn3 <- train(Bijel ~., data=datImprovedSmall[c("Bijel", "Auto.First.Turn", "Rad.Line.Gradients", "Rad.Val.30")], method="knn", trControl=trCtrl, tuneLength=42)
errors[3]=1-knn_fit_turn3$results[row.names(knn_fit_turn3$bestTune),]$Accuracy
set.seed(1234)
knn_fit_turn2 <- train(Bijel ~., data=datImprovedSmall[c("Bijel", "Auto.First.Turn", "Rad.Line.Gradients")], method="knn", trControl=trCtrl, tuneLength=42)
errors[4]=1-knn_fit_turn2$results[row.names(knn_fit_turn2$bestTune),]$Accuracy
set.seed(1234)
knn_fit_turn1 <- train(Bijel ~., data=datImprovedSmall[c("Bijel", "Auto.First.Turn")], method="knn", trControl=trCtrl, tuneLength=42)
errors[5]=1-knn_fit_turn1$results[row.names(knn_fit_turn1$bestTune),]$Accuracy

Plot the results

errors
[1] 0.1775946 0.1869841 0.1774359 0.1774359 0.1639927
png(file='/Users/s1101153/Desktop/bijels1_graphs/error_vs_num_vars.png', width=600, height=400) 
plot(c(5,4,3,2,1), errors, xlab="Number of variables in model", ylab="Classification error", pch=19, ylim=c(.15, .2))
dev.off()
null device 
          1 
pred2 <- predict(knn_fit_turn2, data=datImprovedSmall[c("Bijel", "Auto.First.Turn", "Rad.Line.Gradients")])
png(file='/Users/s1101153/Desktop/bijels1_graphs/liquid_2vars.png', width=600, height=400)  
plot(Auto.First.Turn, Rad.Line.Gradients, col=Bijel, pch=16)
points(Auto.First.Turn, Rad.Line.Gradients, col=pred2, pch=1, cex=1.5)
dev.off()
null device 
          1 
pred1 <- predict(knn_fit_turn1, data=datImprovedSmall[c("Bijel", "Auto.First.Turn")])
set.seed(1234)
y=jitter(rep(0, each=135))
png(file='/Users/s1101153/Desktop/bijels1_graphs/liquid_result.png', width=600, height=400)  
plot(y~Auto.First.Turn, col=Bijel, pch=16, yaxt='n', xlab="Position of first turning point in Liquid Channel ACF (μm)", ylab="", log="x")
points(y~Auto.First.Turn, col=pred1, pch=1, cex=1.5)
legend("topright", legend=c("Bijel", "Non-bijel", "Pred. bijel", "Pred. non-bijel"), pch=c(16,16,1,1), col=c("red", "black", "red", "black"))
dev.off()
null device 
          1 
table()
Error in table() : nothing to tabulate

Final model

final_model <- train(Bijel ~., data=datImprovedSmall[c("Bijel", "Auto.First.Turn")], method="knn", trControl=trCtrl, tuneLength=42)
errors[5]=1-knn_fit_turn1$results[row.names(knn_fit_turn1$bestTune),]$Accuracy
png(file='/Users/s1101153/Desktop/bijels1_graphs/liquid_result_k_graph.png', width=600, height=400)
plot(final_model)
dev.off()
null device 
          1 
pred_final <- predict(final_model, data=datImprovedSmall[c("Bijel", "Auto.First.Turn")])
pred_final
  [1] y y y y n y y y y y n y n y y y y y y n y y n y y y y y n y n y n y y y y y y n y n n y y y y y y y y y y y y y y y y n y y y y
 [65] y y y y n n y n y n y n y y y y n y n y y y y y y y y y y y y y y y y y y y n n y y y y y y y y y y y y n y y n y y y n y y n n
[129] y n y y y y y
Levels: n y
table(Predict=pred_final, true=Bijel)
       true
Predict  n  y
      n 24  3
      y 19 89

Applying the final model to new data

First try brand new, unrelated data

First read in the new files:

corrFilesNew <- list.files("/Volumes/PhD/BijelData/acf_new/0", pattern=".txt", full.names = TRUE)
corrFileNamesNew <- list.files("/Volumes/PhD/BijelData/acf_new/0", pattern=".txt")
autoCorrNew <- do.call(cbind, lapply(corrFilesNew, read.csv, header=FALSE))
colnames(autoCorrNew) <- corrFileNamesNew
exp_DataNew <- read.csv("/Volumes/PhD/BijelData/new_Data/Bijel_Data_Batch1.csv", na.strings = "?")
exp_DataNew$Sample.Number <- as.character(exp_DataNew$Sample.Number)
corrFileIDNew <- sapply(strsplit(corrFileNamesNew,"_"), `[`,1) #`[` is a function that takes the subset of x, the input to this function is x (strsplit...) and the element of x that I want, ie the 1st one
colnames(autoCorrNew) <- corrFileIDNew
rownames(exp_DataNew) <- exp_DataNew$Sample.Number
autoCorrNew_transpose <- data.frame(t(autoCorrNew))
exp_DataNew$Autocorrelation <- autoCorrNew_transpose[match(row.names(exp_DataNew),row.names(autoCorrNew_transpose)),c(1:256)]
num_points <- dim(exp_DataNew)[1]
turningPointsNew <- lapply(1:num_points, function(y) turnpoints(unlist(exp_DataNew$Autocorrelation[y,])))
value out of range in 'gammafn'
exp_DataNew$Auto.Turning.Points <- turningPointsNew
firstTurnNew <- lapply(1:num_points, function(y) exp_DataNew$Auto.Turning.Points[[y]]$tppos[[1]])
exp_DataNew$Auto.First.Turn <- unlist(firstTurnNew)
new_for_model <- exp_DataNew[c(16,19)]

Now apply the final model to the new data:

bijel_pred = predict(final_model, newdata = new_for_model)
bijel_true = new_for_model$Bijel
success_count=length(bijel_pred[bijel_pred==bijel_true])
success_rate=success_count/length(bijel_pred)
paste0("Success rate: ",100*success_rate,"%")
[1] "Success rate: 74.1935483870968%"
null_rate = length(bijel_true[bijel_true=='y'])/length(bijel_pred)
paste0("Null rate: ", 100*null_rate, "%")
[1] "Null rate: 70.9677419354839%"
table(Predict=bijel_pred, true=bijel_true)
       true
Predict  n  y
      n  1  0
      y  8 22
print(data.frame(bijel_pred, bijel_true=bijel_true))

Now repeat that with data more similar to the data the model was trained on:

First read in the new files (from sameSample_analysis.R in Git folder):

bijelFilesL <- list.files("/Volumes/PhD/BijelData/sample52ii/liquid", pattern=".txt", full.names = TRUE)
bijelFileNames <- list.files("/Volumes/PhD/BijelData/sample52ii/liquid", pattern=".txt")
failFilesL <- list.files("/Volumes/PhD/BijelData/sample54i/liquid", pattern=".txt", full.names = TRUE)
failFileNames <- list.files("/Volumes/PhD/BijelData/sample54i/liquid", pattern=".txt")
autoCorrBL <- do.call(cbind, lapply(bijelFilesL, read.csv, header=FALSE))
bijelID <- sapply(strsplit(bijelFileNames,"_"), `[`,1)
bijelLabs <- rep_len("y", length(bijelFileNames))
bDat <- data.frame(Bijel=bijelLabs)
rownames(bDat) <- bijelID
bDat$Liq <- data.frame(t(autoCorrBL))
autoCorrNBL <- do.call(cbind, lapply(failFilesL, read.csv, header=FALSE))
failID <- sapply(strsplit(failFileNames,"_"), `[`,1)
#colnames(autoCorrNBL) <- colnames(autoCorrNBP) <- failID
failLabs <- rep_len("n", length(failFileNames))
nbDat <- data.frame(Bijel=failLabs)
nbDat$Liq <- data.frame(t(autoCorrNBL))
lTurnsB <- lapply(1:8, function(n) turnpoints(unlist(bDat$Liq[n,])))
firstTurnB <- lapply(1:8, function(m) lTurnsB[[m]]$tppos[1])
bDat$Auto.First.Turn <- unlist(firstTurnB)
lTurnsNB <- lapply(1:11, function(n) turnpoints(unlist(nbDat$Liq[n,])))
value out of range in 'gammafn'
firstTurnNB <- lapply(1:11, function(m) lTurnsNB[[m]]$tppos[1])
nbDat$Auto.First.Turn <- unlist(firstTurnNB)
attach(bDat)
The following objects are masked from nbDat (pos = 3):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 4):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 5):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 6):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from exp_Data (pos = 7):

    Auto.First.Turn, Bijel

The following objects are masked from nbDat (pos = 8):

    Bijel, Liq

The following objects are masked from bDat (pos = 9):

    Bijel, Liq

The following objects are masked from bDat (pos = 10):

    Bijel, Liq

The following objects are masked from nbDat (pos = 11):

    Bijel, Liq

The following objects are masked from bDat (pos = 12):

    Bijel, Liq

The following object is masked from exp_Data (pos = 13):

    Bijel

The following object is masked from exp_Data (pos = 14):

    Bijel

The following objects are masked from nbDat (pos = 15):

    Bijel, Liq

The following objects are masked from bDat (pos = 16):

    Bijel, Liq

The following objects are masked from nbDat (pos = 17):

    Bijel, Liq

The following objects are masked from bDat (pos = 18):

    Bijel, Liq

The following object is masked from exp_Data (pos = 19):

    Bijel

The following object is masked from exp_Data (pos = 20):

    Bijel

The following objects are masked from nbDat (pos = 21):

    Bijel, Liq

The following objects are masked from bDat (pos = 22):

    Bijel, Liq

The following object is masked from exp_Data (pos = 23):

    Bijel

The following objects are masked from nbDat (pos = 24):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 25):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 26):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 27):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 28):

    Bijel, Liq

The following objects are masked from bDat (pos = 29):

    Bijel, Liq

The following objects are masked from nbDat (pos = 30):

    Bijel, Liq

The following objects are masked from bDat (pos = 31):

    Bijel, Liq

The following objects are masked from exp_Data (pos = 37):

    Auto.First.Turn, Bijel
testDat1 <- data.frame(Bijel, Auto.First.Turn)
attach(nbDat)
The following objects are masked from bDat (pos = 3):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 4):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 5):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 6):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 7):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from exp_Data (pos = 8):

    Auto.First.Turn, Bijel

The following objects are masked from nbDat (pos = 9):

    Bijel, Liq

The following objects are masked from bDat (pos = 10):

    Bijel, Liq

The following objects are masked from bDat (pos = 11):

    Bijel, Liq

The following objects are masked from nbDat (pos = 12):

    Bijel, Liq

The following objects are masked from bDat (pos = 13):

    Bijel, Liq

The following object is masked from exp_Data (pos = 14):

    Bijel

The following object is masked from exp_Data (pos = 15):

    Bijel

The following objects are masked from nbDat (pos = 16):

    Bijel, Liq

The following objects are masked from bDat (pos = 17):

    Bijel, Liq

The following objects are masked from nbDat (pos = 18):

    Bijel, Liq

The following objects are masked from bDat (pos = 19):

    Bijel, Liq

The following object is masked from exp_Data (pos = 20):

    Bijel

The following object is masked from exp_Data (pos = 21):

    Bijel

The following objects are masked from nbDat (pos = 22):

    Bijel, Liq

The following objects are masked from bDat (pos = 23):

    Bijel, Liq

The following object is masked from exp_Data (pos = 24):

    Bijel

The following objects are masked from nbDat (pos = 25):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 26):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 27):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from bDat (pos = 28):

    Auto.First.Turn, Bijel, Liq

The following objects are masked from nbDat (pos = 29):

    Bijel, Liq

The following objects are masked from bDat (pos = 30):

    Bijel, Liq

The following objects are masked from nbDat (pos = 31):

    Bijel, Liq

The following objects are masked from bDat (pos = 32):

    Bijel, Liq

The following objects are masked from exp_Data (pos = 38):

    Auto.First.Turn, Bijel
testDat2 <- data.frame(Bijel, Auto.First.Turn)
testDat <- rbind(testDat1, testDat2)
testDat

Now apply the final model to the new data:

bijel_pred = predict(final_model, newdata = testDat)
bijel_true = testDat$Bijel
length(bijel_pred)
[1] 19
length(bijel_true)
[1] 19
success_count=length(bijel_pred[bijel_pred==bijel_true])
success_count
[1] 12
success_rate=success_count/length(bijel_pred)
paste0("Success rate: ",100*success_rate,"%")
[1] "Success rate: 63.1578947368421%"
null_rate = 1-(length(bijel_true[bijel_true=='y'])/length(bijel_pred))
paste0("Null rate: ", 100*null_rate, "%")
[1] "Null rate: 57.8947368421053%"
table(Predict=bijel_pred, true=bijel_true)
       true
Predict y n
      n 0 4
      y 8 7
print(data.frame(bijel_pred, bijel_true=bijel_true))
LS0tCnRpdGxlOiAiRmluYWwgc3RvcnkgZm9yIHRoZXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKRmlyc3QgbG9vayBhdCB0aGUgc2V2ZW4gdmFyaWFibGVzIEkgY2hvc2UgaW4gdGVybXMgb2YgYm94LWFuZC1qaXR0ZXIgcGxvdHM6CgpBdXRvY29ycmVsYXRpb24gZnVuY3Rpb246CgoqIE51bWJlciBvZiB0dXJuaW5nIHBvaW50cwoqIERlcHRoIG9mIGZpcnN0IHRyb3VnaC9wZWFrIHBhaXIKKiBMb2NhdGlvbiBvZiBmaXJzdCB0dXJuaW5nIHBvaW50CgoKYGBge3J9CmxpYnJhcnkocGFzdGVjcykKCmNvcnJGaWxlcyA8LSBsaXN0LmZpbGVzKCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL0JhdGNoX0lJX2Z1bGxfcmVzb2x1dGlvbl9FRF9pbWFnZXMvQXV0b2NvcnJlbGF0aW9uIiwgcGF0dGVybj0iLnR4dCIsIGZ1bGwubmFtZXMgPSBUUlVFKQpjb3JyRmlsZU5hbWVzIDwtIGxpc3QuZmlsZXMoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvQmF0Y2hfSUlfZnVsbF9yZXNvbHV0aW9uX0VEX2ltYWdlcy9BdXRvY29ycmVsYXRpb24iLCBwYXR0ZXJuPSIudHh0IikKCmF1dG9Db3JyIDwtIGRvLmNhbGwoY2JpbmQsIGxhcHBseShjb3JyRmlsZXMsIHJlYWQuY3N2LCBoZWFkZXI9RkFMU0UpKQpjb2xuYW1lcyhhdXRvQ29ycikgPC0gY29yckZpbGVOYW1lcwoKZXhwX0RhdGEgPC0gcmVhZC5jc3YoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvQmlqZWxfRGF0YV9DbGVhbmVyX1RvUmVhZC5jc3YiLCBuYS5zdHJpbmdzID0gIj8iKQpleHBfRGF0YSRTYW1wbGUuTnVtYmVyIDwtIGFzLmNoYXJhY3RlcihleHBfRGF0YSRTYW1wbGUuTnVtYmVyKQoKY29yckZpbGVJRCA8LSBzYXBwbHkoc3Ryc3BsaXQoY29yckZpbGVOYW1lcywiXyIpLCBgW2AsMSkgI2BbYCBpcyBhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgdGhlIHN1YnNldCBvZiB4LCB0aGUgaW5wdXQgdG8gdGhpcyBmdW5jdGlvbiBpcyB4IChzdHJzcGxpdC4uLikgYW5kIHRoZSBlbGVtZW50IG9mIHggdGhhdCBJIHdhbnQsIGllIHRoZSAxc3Qgb25lCmNvbG5hbWVzKGF1dG9Db3JyKSA8LSBjb3JyRmlsZUlECgpyb3duYW1lcyhleHBfRGF0YSkgPC0gZXhwX0RhdGEkU2FtcGxlLk51bWJlcgphdXRvQ29ycl90cmFuc3Bvc2UgPC0gZGF0YS5mcmFtZSh0KGF1dG9Db3JyKSkKZXhwX0RhdGEkQXV0b2NvcnJlbGF0aW9uIDwtIGF1dG9Db3JyX3RyYW5zcG9zZVttYXRjaChyb3cubmFtZXMoZXhwX0RhdGEpLHJvdy5uYW1lcyhhdXRvQ29ycl90cmFuc3Bvc2UpKSxjKDE6MjU2KV0KCgp0dXJuaW5nUG9pbnRzIDwtIGxhcHBseSgxOjEzNSwgZnVuY3Rpb24oeSkgdHVybnBvaW50cyh1bmxpc3QoZXhwX0RhdGEkQXV0b2NvcnJlbGF0aW9uW3ksXSkpKQpleHBfRGF0YSRBdXRvLlR1cm5pbmcuUG9pbnRzIDwtIHR1cm5pbmdQb2ludHMKCmZpcnN0VHVybiA8LSBsYXBwbHkoMToxMzUsIGZ1bmN0aW9uKHkpIGV4cF9EYXRhJEF1dG8uVHVybmluZy5Qb2ludHNbW3ldXSR0cHBvc1tbMV1dKQpleHBfRGF0YSRBdXRvLkZpcnN0LlR1cm4gPC0gdW5saXN0KGZpcnN0VHVybikKCnBlYWtEZXB0aCA8LSBsYXBwbHkoMToxMzUsIGZ1bmN0aW9uKHkpIGV4cF9EYXRhJEF1dG9jb3JyZWxhdGlvbltbeSxleHBfRGF0YSRBdXRvLlR1cm5pbmcuUG9pbnRzW1t5XV0kdHBwb3NbWzJdXV1dLWV4cF9EYXRhJEF1dG9jb3JyZWxhdGlvbltbeSxleHBfRGF0YSRBdXRvLlR1cm5pbmcuUG9pbnRzW1t5XV0kdHBwb3NbWzFdXV1dKQpleHBfRGF0YSRBdXRvLlBlYWsuRGVwdGggPC0gdW5saXN0KHBlYWtEZXB0aCkKCgpleHBfRGF0YSRBdXRvLk51bS5UdXJucyA8LSB1bmxpc3QobGFwcGx5KDE6MTM1LCBmdW5jdGlvbih5KSBsZW5ndGgoZXhwX0RhdGEkQXV0by5UdXJuaW5nLlBvaW50c1tbeV1dJHRwcG9zKSkpCgpgYGAKCgoKUmFkaWFsIHByb2ZpbGU6CgoqIEF2ZXJhZ2Ugb2YgcG9pbnRzIDI5LTMxCiogR3JhZGllbnQgb2YgcG9pbnRzIDEtMjAKKiBTaXplIG9mIGJpZ2dlc3QgdXB3YXJkIGp1bXAgaW4gdGhlIHByb2ZpbGUKKiBSLXBvc2l0aW9uIG9mIGJpZ2dlc3QgdXB3YXJkIGp1bXAgaW4gdGhlIHByb2ZpbGUKCmBgYHtyfQpyYWRGaWxlcyA8LSBsaXN0LmZpbGVzKCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL0JhdGNoX0lJX2Z1bGxfcmVzb2x1dGlvbl9FRF9pbWFnZXMvUHJvZmlsZXMiLCBwYXR0ZXJuPSIudHh0IiwgZnVsbC5uYW1lcyA9IFRSVUUpCnJhZEZpbGVOYW1lcyA8LSBsaXN0LmZpbGVzKCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL0JhdGNoX0lJX2Z1bGxfcmVzb2x1dGlvbl9FRF9pbWFnZXMvUHJvZmlsZXMiLCBwYXR0ZXJuPSIudHh0IikKCnJhZFByb2ZfZGF0YSA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkocmFkRmlsZXMsIHJlYWQuY3N2LCBoZWFkZXI9RkFMU0UpKQpjb2xuYW1lcyhyYWRQcm9mX2RhdGEpIDwtIHJhZEZpbGVOYW1lcwoKcmFkRmlsZUlEIDwtIHNhcHBseShzdHJzcGxpdChyYWRGaWxlTmFtZXMsIl8iKSwgYFtgLDEpICNgW2AgaXMgYSBmdW5jdGlvbiB0aGF0IHRha2VzIHRoZSBzdWJzZXQgb2YgeCwgdGhlIGlucHV0IHRvIHRoaXMgZnVuY3Rpb24gaXMgeCAoc3Ryc3BsaXQuLi4pIGFuZCB0aGUgZWxlbWVudCBvZiB4IHRoYXQgSSB3YW50LCBpZSB0aGUgMXN0IG9uZQpjb2xuYW1lcyhyYWRQcm9mX2RhdGEpIDwtIHJhZEZpbGVJRAoKcm93bmFtZXMoZXhwX0RhdGEpIDwtIGV4cF9EYXRhJFNhbXBsZS5OdW1iZXIKcmFkUHJvZl9kYXRhX3RyYW5zcG9zZSA8LSBkYXRhLmZyYW1lKHQocmFkUHJvZl9kYXRhKSkKZXhwX0RhdGEkUmFkaWFsLlByb2ZpbGUgPC0gcmFkUHJvZl9kYXRhX3RyYW5zcG9zZVttYXRjaChyb3cubmFtZXMoZXhwX0RhdGEpLHJvdy5uYW1lcyhyYWRQcm9mX2RhdGFfdHJhbnNwb3NlKSksYygxOjI1NildCgoKCnIgPC0gYygxOjI1NikKeSA8LSBleHBfRGF0YSRSYWRpYWwuUHJvZmlsZVsyOjIwXQpsaW5lRml0cyA8LSBsYXBwbHkoMToxMzUsIGZ1bmN0aW9uKG4pIGxtKHVubGlzdCh5W24sXSkgfiByWzI6MjBdKSkKbGluZUNvZWZmcyA8LSBsYXBwbHkobGluZUZpdHMsIGZ1bmN0aW9uKG0pIG0kY29lZmZpY2llbnRzKQpsaW5lR3JhZGllbnRzIDwtIGxhcHBseSAoMToxMzUsIGZ1bmN0aW9uKHApIHVubmFtZShsaW5lQ29lZmZzW1twXV1bMl0pKQpleHBfRGF0YSRSYWQuTGluZS5HcmFkaWVudHMgPC0gdW5saXN0KGxpbmVHcmFkaWVudHMpCgoKYXZWYWwgPC0gbGFwcGx5KDE6MTM1LCBmdW5jdGlvbihuKSBtZWFuKHVubGlzdChleHBfRGF0YSRSYWRpYWwuUHJvZmlsZVtuLDI5OjMxXSkpKQpleHBfRGF0YSRSYWQuVmFsLjMwIDwtIHVubGlzdChhdlZhbCkKCgpqdW1wcyA8LSBmdW5jdGlvbih5LG4pewogIGEgPC0gZXhwX0RhdGEkUmFkaWFsLlByb2ZpbGVbW3ksbis1XV0KICBiIDwtIGV4cF9EYXRhJFJhZGlhbC5Qcm9maWxlW1t5LG5dXQogIG91dCA8LSBhLWIKICByZXR1cm4ob3V0KQp9Cgojbk1heCA8LSBsYXBwbHkoMToxMzUsIGZ1bmN0aW9uKHkpIGxlbmd0aChleHBfRGF0YSRSYWRpYWwuUHJvZmlsZVtbeV1dKS0xKQpqdW1wU2l6ZXMgPC0gbGFwcGx5KDE6MTM1LCBmdW5jdGlvbih5KSBtYXBwbHkoanVtcHMsIHksIDE6MjUxKSkKbWF4SnVtcCA8LSBsYXBwbHkoMToxMzUsIGZ1bmN0aW9uKHkpIG1heChqdW1wU2l6ZXNbW3ldXSkpCmV4cF9EYXRhJE1heC5SYWQuSnVtcCA8LSB1bmxpc3QobWF4SnVtcCkKCgptYXhKdW1wWCA8LSBsYXBwbHkoMToxMzUsIGZ1bmN0aW9uKHkpIHdoaWNoLm1heChqdW1wU2l6ZXNbW3ldXSkpCmV4cF9EYXRhJE1heC5SYWQuSnVtcC5YIDwtIHVubGlzdChtYXhKdW1wWCkKCgpgYGAKClBsb3QgdGhlbSBhbGwuCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCnBuZyhmaWxlPScvVXNlcnMvczExMDExNTMvRGVza3RvcC9iaWplbHMxX2dyYXBocy9ianBfYXV0by1udW0tdHVybnMucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAKZ2dwbG90KGV4cF9EYXRhLCBhZXMoeD1hcy5mYWN0b3IoQmlqZWwpLCB5PUF1dG8uTnVtLlR1cm5zLCBmaWxsPUJpamVsKSkgKyBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSArIGdlb21faml0dGVyKGFscGhhPTAuNSkgKyAgeGxhYigiQmlqZWw/IikgKyB5bGFiKCJOdW1iZXIgb2YgdHVybmluZyBwb2ludHMgaW4gQUNGIikrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTI0KSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIxKSkKZGV2Lm9mZigpCgpwbmcoZmlsZT0nL1VzZXJzL3MxMTAxMTUzL0Rlc2t0b3AvYmlqZWxzMV9ncmFwaHMvYmpwX2F1dG8tcGVhay1kZXB0aC5wbmcnLCB3aWR0aD02MDAsIGhlaWdodD00MDApIApnZ3Bsb3QoZXhwX0RhdGEsIGFlcyh4PWFzLmZhY3RvcihCaWplbCksIHk9QXV0by5QZWFrLkRlcHRoLCBmaWxsPUJpamVsKSkgKyBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSArIGdlb21faml0dGVyKGFscGhhPTAuNSkgKyAgeGxhYigiQmlqZWw/IikgKyB5bGFiKCJEZXB0aCBvZiBmaXJzdCBBQ0YgdHJvdWdoL3BlYWsgcGFpciIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yNCksIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMSkpCmRldi5vZmYoKQoKcG5nKGZpbGU9Jy9Vc2Vycy9zMTEwMTE1My9EZXNrdG9wL2JpamVsczFfZ3JhcGhzL2JqcF9hdXRvLWZpcnN0LXR1cm4ucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAKZ2dwbG90KGV4cF9EYXRhLCBhZXMoeD1hcy5mYWN0b3IoQmlqZWwpLCB5PUF1dG8uRmlyc3QuVHVybiwgZmlsbD1CaWplbCkpICsgZ2VvbV9ib3hwbG90KGFscGhhPTAuMykgKyBnZW9tX2ppdHRlcihhbHBoYT0wLjUpICsKICB4bGFiKCJCaWplbD8iKSArIHlsYWIoIlBvc2l0aW9uIG9mIGZpcnN0IEFDRiB0dXJuaW5nIHBvaW50IikrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTI0KSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIxKSkKZGV2Lm9mZigpCgpwbmcoZmlsZT0nL1VzZXJzL3MxMTAxMTUzL0Rlc2t0b3AvYmlqZWxzMV9ncmFwaHMvYmpwX3JhZC12YWwtMzAucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAKZ2dwbG90KGV4cF9EYXRhLCBhZXMoeD1hcy5mYWN0b3IoQmlqZWwpLCB5PVJhZC5WYWwuMzAsIGZpbGw9QmlqZWwpKSArIGdlb21fYm94cGxvdChhbHBoYT0wLjMpICsgZ2VvbV9qaXR0ZXIoYWxwaGE9MC41KSArICB4bGFiKCJCaWplbD8iKSArIHlsYWIoIkF2ZXJhZ2Ugb2YgcG9pbnRzIDI5LTMxIG9mIFNGIikrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTI0KSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIxKSkKZGV2Lm9mZigpCgpwbmcoZmlsZT0nL1VzZXJzL3MxMTAxMTUzL0Rlc2t0b3AvYmlqZWxzMV9ncmFwaHMvYmpwX3JhZC1saW5lLWdyYWRpZW50cy5wbmcnLCB3aWR0aD02MDAsIGhlaWdodD00MDApIApnZ3Bsb3QoZXhwX0RhdGEsIGFlcyh4PWFzLmZhY3RvcihCaWplbCksIHk9UmFkLkxpbmUuR3JhZGllbnRzLCBmaWxsPUJpamVsKSkgKyBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSArIGdlb21faml0dGVyKGFscGhhPTAuNSkgKyAgeGxhYigiQmlqZWw/IikgKyB5bGFiKCJHcmFkaWVudCBvZiBmaXJzdCAyMCBwb2ludHMgb2YgU0YiKSt0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjQpLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjEpKQpkZXYub2ZmKCkKCnBuZyhmaWxlPScvVXNlcnMvczExMDExNTMvRGVza3RvcC9iaWplbHMxX2dyYXBocy9ianBfcmFkLW1heC1qdW1wLnBuZycsIHdpZHRoPTYwMCwgaGVpZ2h0PTQwMCkgCmdncGxvdChleHBfRGF0YSwgYWVzKHg9YXMuZmFjdG9yKEJpamVsKSwgeT1NYXguUmFkLkp1bXAsIGZpbGw9QmlqZWwpKSArIGdlb21fYm94cGxvdChhbHBoYT0wLjMpICsgZ2VvbV9qaXR0ZXIoYWxwaGE9MC41KSArICB4bGFiKCJCaWplbD8iKSArIHlsYWIoIkhlaWdodCBvZiBiaWdnZXN0IHBvc2l0aXZlIGp1bXAgaW4gU0YiKSt0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjQpLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjEpKQpkZXYub2ZmKCkKCnBuZyhmaWxlPScvVXNlcnMvczExMDExNTMvRGVza3RvcC9iaWplbHMxX2dyYXBocy9ianBfcmFkLW1heC1qdW1wLXgucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAKZ2dwbG90KGV4cF9EYXRhLCBhZXMoeD1hcy5mYWN0b3IoQmlqZWwpLCB5PU1heC5SYWQuSnVtcC5YLCBmaWxsPUJpamVsKSkgKyBnZW9tX2JveHBsb3QoYWxwaGE9MC4zKSArIGdlb21faml0dGVyKGFscGhhPTAuNSkgKyAgeGxhYigiQmlqZWw/IikgKyB5bGFiKCJQb3NpdGlvbiAoaW4gcikgb2YgYmlnZ2VzdCBqdW1wIGluIFNGIikrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTI0KSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIxKSkKZGV2Lm9mZigpCgpgYGAKClBsb3QgZXhhbXBsZXMgb2YgdGhlIEFDRiBhbmQgU0YgdG9vOgpgYGB7cn0KeD1jKDE6MjU1KQp4U0Y9eCo2NDAuMTcvNTEyCkwgPC0gNjQwLjE3CnBpeGVsX3RvX21pY3JvbiA8LSAyKnBpL0wKI2V4YW1wbGUgU0YgcGxvdHMgKGxpcXVpZCBjaGFubmVsKQpiaWplbFNGX2wgPC0gdW5saXN0KHJlYWQuY3N2KCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL1NGX2JvdGhDaGFubmVscy81MmlpX0ltYWdlMjMudGlmX3JhZFByb2ZfY2hhbm5lbDAudHh0IikpCm5vQmlqZWxTRl9sIDwtIHVubGlzdChyZWFkLmNzdigiL1ZvbHVtZXMvUGhEL0JpamVsRGF0YS9TRl9ib3RoQ2hhbm5lbHMvNTRpX0ltYWdlOS50aWZfcmFkUHJvZl9jaGFubmVsMC50eHQiKSkKcG5nKCd+L0Rlc2t0b3AvYmlqZWxzMV9ncmFwaHMvU0ZleGFtcGxlc19saXEucG5nJywgcmVzPTMwMCwgd2lkdGg9MTgwMCwgaGVpZ2h0PTEyMDApCnBsb3QoeFNGLCBiaWplbFNGX2wsIHR5cGU9ImwiLCBsd2Q9MiwgY29sPSJyZWQiLCB4bGFiID0gInEgKDEvzrxtKSIsIHlsYWI9IlN0cnVjdHVyZSBGYWN0b3IiLCB5bGltPWMoMC40LDIuMiksY2V4LmF4aXM9MS41LGNleC5sYWI9MixtZ3A9YygyLjIsMC43LDApKQpsaW5lcyh4U0YsIG5vQmlqZWxTRl9sLCBsd2Q9MikKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIkJpamVsIiwgIk5vbi1iaWplbCIpLCBjb2w9YygicmVkIiwgImJsYWNrIiksIGx3ZD0yKSMsIGNleD0xLjQpCmRldi5vZmYoKQoKI2V4YW1wbGUgQUNGIHBsb3RzIChsaXF1aWQgY2hhbm5lbCkKYmlqZWxBQ0ZfbCA8LSB1bmxpc3QocmVhZC5jc3YoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvTGlxdWlkQ2hhbm5lbC9hdXRvQ29yci81MmlpX0ltYWdlMjMudGlmX2F1dG9Db3JyX2NoYW5uZWwwLnR4dCIpKQpub0JpamVsQUNGX2wgPC0gdW5saXN0KHJlYWQuY3N2KCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL0xpcXVpZENoYW5uZWwvYXV0b0NvcnIvNTRpX0ltYWdlOS50aWZfYXV0b0NvcnJfY2hhbm5lbDAudHh0IikpCng9YygxOjI1NSkKeEFDRj14KnBpeGVsX3RvX21pY3JvbgpwbmcoJ34vRGVza3RvcC9iaWplbHMxX2dyYXBocy9BQ0ZleGFtcGxlc19saXEucG5nJywgcmVzPTMwMCwgd2lkdGg9MTgwMCwgaGVpZ2h0PTEyMDApCnBsb3QoeEFDRiwgYmlqZWxBQ0ZfbCwgdHlwZT0ibCIsIGx3ZD0yLCBjb2w9InJlZCIsIHhsYWIgPSAiciAozrxtKSIsIHlsYWI9IkF1dG9jb3JyZWxhdGlvbiBGdW5jdGlvbiIsY2V4LmF4aXM9MS41LGNleC5sYWI9MixtZ3A9YygyLjIsMC43LDApKQpsaW5lcyh4QUNGLCBub0JpamVsQUNGX2wsIGx3ZD0yKQpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gYygiQmlqZWwiLCAiTm9uLWJpamVsIiksIGNvbD1jKCJyZWQiLCAiYmxhY2siKSwgbHdkPTIpCmRldi5vZmYoKQpgYGAKCgoKTm93IHNjYWxlIHRoZSBkYXRhIGZvciBhY2N1cmF0ZSByZXN1bHRzOgoKYGBge3J9CmF0dGFjaChleHBfRGF0YSkKZGF0PWRhdGEuZnJhbWUoQXV0by5OdW0uVHVybnMsIEF1dG8uUGVhay5EZXB0aCwgUmFkLlZhbC4zMCwgUmFkLkxpbmUuR3JhZGllbnRzLCBNYXguUmFkLkp1bXAsIE1heC5SYWQuSnVtcC5YLCBBdXRvLkZpcnN0LlR1cm4pCgptZWFucyA9IHVubGlzdChsYXBwbHkoMTo3LCBmdW5jdGlvbihuKSBtZWFuKHVubGlzdChkYXRbbl1bXSkpKSkKCmRhdFNjYWxlZCA9IGRhdGEuZnJhbWUobGFwcGx5KDE6NywgZnVuY3Rpb24obikgZGF0W25dW10qbWVhbnNbMV0vbWVhbnNbbl0pKQpkYXRTY2FsZWQkQmlqZWwgPSBCaWplbApoZWFkKGRhdFNjYWxlZCkKCmBgYAoKTm93IHJ1biBkZWNpc2lvbiB0cmVlIChpZ25vcmUgdGhpcyBmb3Igbm93Li4uKQpgYGB7cn0KbGlicmFyeShjYXJldCkKbGlicmFyeShycGFydC5wbG90KQoKCnNldC5zZWVkKDEyMzQpCgp0ckN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0icmVwZWF0ZWRjdiIsIG51bWJlcj0xMCwgcmVwZWF0cyA9IDMpCmR0cmVlRml0IDwtIHRyYWluKEJpamVsIH4uLCBkYXRhPWRhdFNjYWxlZCwgbWV0aG9kPSJycGFydCIsIHBhcm1zPWxpc3Qoc3BsaXQ9ImdpbmkiKSwgdHJDb250cm9sPXRyQ3RybCwgdHVuZUxlbmd0aD0xMCkKCmR0cmVlRml0CnBycChkdHJlZUZpdCRmaW5hbE1vZGVsLCBib3gucGFsZXR0ZSA9ICJSZWRzIiwgdHdlYWs9MS4yLCBleHRyYT0xMDEpCgojIHRhYmxlKFByZWRpY3Q9ZHRyZWVGaXQkcHJlZChkYXRTY2FsZWQpLCB0cnVlPUJpamVsKQoKYGBgCgoKTm93IGtubjoKYGBge3J9Cgoja25uIC0gYWxsIDcgdmFyaWFibGVzCnNldC5zZWVkKDEyMzQpCmtubl9maXQgPC0gdHJhaW4oQmlqZWwgfi4sIGRhdGE9ZGF0U2NhbGVkLCBtZXRob2Q9ImtubiIsIHRyQ29udHJvbD10ckN0cmwsIHR1bmVMZW5ndGg9NDIpCmVycm9yPTEta25uX2ZpdCRyZXN1bHRzW3Jvdy5uYW1lcyhrbm5fZml0JGJlc3RUdW5lKSxdJEFjY3VyYWN5CmVycm9yCmtubl9maXQKcGxvdChrbm5fZml0KQoKcGFpcnMoZGF0U2NhbGVkWy04XSwgY29sPUJpamVsLCBtYWluID0gIkRhdGEiKQoKYmlqZWxQcmVkIDwtIHByZWRpY3Qoa25uX2ZpdCwgZGF0U2NhbGVkKQpwYWlycyhkYXRTY2FsZWRbLThdLCBjb2w9YmlqZWxQcmVkLCBtYWluPSJQcmVkaWN0ZWQiKQoKYGBgCgoKIyBUcnkgcmVtb3ZpbmcgdmFyaWFibGVzCgoKYGBge3J9CiNBdXRvLk51bS5UdXJucyByZW1vdmVkCnNldC5zZWVkKDEyMzQpCmtubl9maXQgPC0gdHJhaW4oQmlqZWwgfi4sIGRhdGE9ZGF0U2NhbGVkWy0xXSwgbWV0aG9kPSJrbm4iLCB0ckNvbnRyb2w9dHJDdHJsLCB0dW5lTGVuZ3RoPTQyKQplcnJvcj0xLWtubl9maXQkcmVzdWx0c1tyb3cubmFtZXMoa25uX2ZpdCRiZXN0VHVuZSksXSRBY2N1cmFjeQplcnJvcgpwbG90KGtubl9maXQpCgpwYWlycyhkYXRTY2FsZWRbMjo3XSwgY29sPUJpamVsLCBtYWluID0gIkRhdGEiKQoKYmlqZWxQcmVkIDwtIHByZWRpY3Qoa25uX2ZpdCwgZGF0U2NhbGVkWy0xXSkKcGFpcnMoZGF0U2NhbGVkWzI6N10sIGNvbD1iaWplbFByZWQsIG1haW49IlByZWRpY3RlZCIpCgpgYGAKCgpgYGB7cn0KI0F1dG8uUGVhay5EZXB0aCByZW1vdmVkCnNldC5zZWVkKDEyMzQpCmtubl9maXQgPC0gdHJhaW4oQmlqZWwgfi4sIGRhdGE9ZGF0U2NhbGVkWy0yXSwgbWV0aG9kPSJrbm4iLCB0ckNvbnRyb2w9dHJDdHJsLCB0dW5lTGVuZ3RoPTQyKQplcnJvcj0xLWtubl9maXQkcmVzdWx0c1tyb3cubmFtZXMoa25uX2ZpdCRiZXN0VHVuZSksXSRBY2N1cmFjeQplcnJvcgpwbG90KGtubl9maXQpCgpwYWlycyhkYXRTY2FsZWRbYygxLDM6NyldLCBjb2w9QmlqZWwsIG1haW4gPSAiRGF0YSIpCgpiaWplbFByZWQgPC0gcHJlZGljdChrbm5fZml0LCBkYXRTY2FsZWRbLTJdKQpwYWlycyhkYXRTY2FsZWRbYygxLDM6NyldLCBjb2w9YmlqZWxQcmVkLCBtYWluPSJQcmVkaWN0ZWQiKQoKYGBgCgoKYGBge3J9CiNSYWQuVmFsLjMwIHJlbW92ZWQKc2V0LnNlZWQoMTIzNCkKa25uX2ZpdCA8LSB0cmFpbihCaWplbCB+LiwgZGF0YT1kYXRTY2FsZWRbLTNdLCBtZXRob2Q9ImtubiIsIHRyQ29udHJvbD10ckN0cmwsIHR1bmVMZW5ndGg9NDIpCmVycm9yPTEta25uX2ZpdCRyZXN1bHRzW3Jvdy5uYW1lcyhrbm5fZml0JGJlc3RUdW5lKSxdJEFjY3VyYWN5CmVycm9yCnBsb3Qoa25uX2ZpdCkKCnBhaXJzKGRhdFNjYWxlZFtjKDE6Miw0OjcpXSwgY29sPUJpamVsLCBtYWluID0gIkRhdGEiKQoKYmlqZWxQcmVkIDwtIHByZWRpY3Qoa25uX2ZpdCwgZGF0U2NhbGVkWy0zXSkKcGFpcnMoZGF0U2NhbGVkW2MoMToyLDQ6NyldLCBjb2w9YmlqZWxQcmVkLCBtYWluPSJQcmVkaWN0ZWQiKQoKYGBgCgpgYGB7cn0KI1JhZC5MaW5lLkdyYWRpZW50cyByZW1vdmVkCnNldC5zZWVkKDEyMzQpCmtubl9maXQgPC0gdHJhaW4oQmlqZWwgfi4sIGRhdGE9ZGF0U2NhbGVkWy00XSwgbWV0aG9kPSJrbm4iLCB0ckNvbnRyb2w9dHJDdHJsLCB0dW5lTGVuZ3RoPTQyKQplcnJvcj0xLWtubl9maXQkcmVzdWx0c1tyb3cubmFtZXMoa25uX2ZpdCRiZXN0VHVuZSksXSRBY2N1cmFjeQplcnJvcgpwbG90KGtubl9maXQpCgpwYWlycyhkYXRTY2FsZWRbYygxOjMsNTo3KV0sIGNvbD1CaWplbCwgbWFpbiA9ICJEYXRhIikKCmJpamVsUHJlZCA8LSBwcmVkaWN0KGtubl9maXQsIGRhdFNjYWxlZFstNF0pCnBhaXJzKGRhdFNjYWxlZFtjKDE6Myw1OjcpXSwgY29sPWJpamVsUHJlZCwgbWFpbj0iUHJlZGljdGVkIikKCmBgYAoKYGBge3J9CiNNYXguUmFkLkp1bXAgcmVtb3ZlZApzZXQuc2VlZCgxMjM0KQprbm5fZml0IDwtIHRyYWluKEJpamVsIH4uLCBkYXRhPWRhdFNjYWxlZFstNV0sIG1ldGhvZD0ia25uIiwgdHJDb250cm9sPXRyQ3RybCwgdHVuZUxlbmd0aD00MikKZXJyb3I9MS1rbm5fZml0JHJlc3VsdHNbcm93Lm5hbWVzKGtubl9maXQkYmVzdFR1bmUpLF0kQWNjdXJhY3kKZXJyb3IKcGxvdChrbm5fZml0KQoKcGFpcnMoZGF0U2NhbGVkW2MoMTo0LDY6NyldLCBjb2w9QmlqZWwsIG1haW4gPSAiRGF0YSIpCgpiaWplbFByZWQgPC0gcHJlZGljdChrbm5fZml0LCBkYXRTY2FsZWRbLTVdKQpwYWlycyhkYXRTY2FsZWRbYygxOjQsNjo3KV0sIGNvbD1iaWplbFByZWQsIG1haW49IlByZWRpY3RlZCIpCgpgYGAKCmBgYHtyfQojTWF4LlJhZC5KdW1wLlggcmVtb3ZlZApzZXQuc2VlZCgxMjM0KQprbm5fZml0IDwtIHRyYWluKEJpamVsIH4uLCBkYXRhPWRhdFNjYWxlZFstNl0sIG1ldGhvZD0ia25uIiwgdHJDb250cm9sPXRyQ3RybCwgdHVuZUxlbmd0aD00MikKZXJyb3I9MS1rbm5fZml0JHJlc3VsdHNbcm93Lm5hbWVzKGtubl9maXQkYmVzdFR1bmUpLF0kQWNjdXJhY3kKZXJyb3IKcGxvdChrbm5fZml0KQoKcGFpcnMoZGF0U2NhbGVkW2MoMTo1LDcpXSwgY29sPUJpamVsLCBtYWluID0gIkRhdGEiKQoKYmlqZWxQcmVkIDwtIHByZWRpY3Qoa25uX2ZpdCwgZGF0U2NhbGVkWy02XSkKcGFpcnMoZGF0U2NhbGVkW2MoMTo1LDcpXSwgY29sPWJpamVsUHJlZCwgbWFpbj0iUHJlZGljdGVkIikKCmBgYAoKYGBge3J9CiNBdXRvLkZpcnN0LlR1cm5zIHJlbW92ZWQKc2V0LnNlZWQoMTIzNCkKa25uX2ZpdCA8LSB0cmFpbihCaWplbCB+LiwgZGF0YT1kYXRTY2FsZWRbLTddLCBtZXRob2Q9ImtubiIsIHRyQ29udHJvbD10ckN0cmwsIHR1bmVMZW5ndGg9NDIpCmVycm9yPTEta25uX2ZpdCRyZXN1bHRzW3Jvdy5uYW1lcyhrbm5fZml0JGJlc3RUdW5lKSxdJEFjY3VyYWN5CmVycm9yCnBsb3Qoa25uX2ZpdCkKCnBhaXJzKGRhdFNjYWxlZFtjKDE6NyldLCBjb2w9QmlqZWwsIG1haW4gPSAiRGF0YSIpCgpiaWplbFByZWQgPC0gcHJlZGljdChrbm5fZml0LCBkYXRTY2FsZWRbLTddKQpwYWlycyhkYXRTY2FsZWRbYygxOjcpXSwgY29sPWJpamVsUHJlZCwgbWFpbj0iUHJlZGljdGVkIikKCmBgYAoKTm93IHJlbW92ZSBhbGwgdGhlIHZhcmlhYmxlcyB3aG9zZSByZW1vdmFsIG1ha2VzIHRoZSBlcnJvciBsb3dlciB0aGFuIHRoZSBvcmlnaW5hbCB2YWx1ZSBvZiAwLjIxNDk1NzM6CgoqIHNpZ25pZmljYW50IGNoYW5nZTogTWF4LlJhZC5KdW1wLlggKHBvc2l0aW9uIDYpCiogc21hbGwgY2hhbmdlOiBNYXguUmFkLkp1bXAgKHBvc2l0aW9uIDUpCiogYWxsIG90aGVycyA8MC4xJSBkZWNyZWFzZSBvciBhbiBpbmNyZWFzZQoKYGBge3J9CmRhdEltcHJvdmVkID0gZGF0U2NhbGVkWy02XQoKZGF0SW1wcm92ZWRTbWFsbCA9IGRhdEltcHJvdmVkWy01XQpgYGAKCgoKIyBGLXN0YXRpc3RpYyBjYWxjdWxhdGlvbnMKCmBgYHtyfQoKdmFycyA8LSBuYW1lcyhkYXRJbXByb3ZlZCkKCnZhcl9mc3RhdCA8LSBzYXBwbHkodmFycywgZnVuY3Rpb24oeCkgewogICAgc3VtbWFyeShsbShhcy5mb3JtdWxhKHBhc3RlKHgsICIgfiBCaWplbCIpKSwgZGF0YSA9IGRhdEltcHJvdmVkKSkkZnN0YXRpc3RpY1sxXQp9KQoKCnNvcnQodW5saXN0KHZhcl9mc3RhdCksIGRlY3JlYXNpbmc9VFJVRSkKCnZhcnMgPC0gbmFtZXMoZGF0SW1wcm92ZWRTbWFsbCkKCnZhcl9mc3RhdCA8LSBzYXBwbHkodmFycywgZnVuY3Rpb24oeCkgewogICAgc3VtbWFyeShsbShhcy5mb3JtdWxhKHBhc3RlKHgsICIgfiBCaWplbCIpKSwgZGF0YSA9IGRhdEltcHJvdmVkU21hbGwpKSRmc3RhdGlzdGljWzFdCn0pCgoKc29ydCh1bmxpc3QodmFyX2ZzdGF0KSwgZGVjcmVhc2luZz1UUlVFKQoKYGBgCnN0aWNrIHdpdGggcmVtb3ZpbmcgdGhlIHR3byB2YXJpYWJsZXMsIGkuZS4gdXNlIGRhdEltcHJvdmVkU21hbGwKCiMgTW9kZWwgbWluaW1pc2F0aW9uCgpgYGB7cn0KCmVycm9ycyA8LSByZXAoTkEsIDUpCnNldC5zZWVkKDEyMzQpCmtubl9maXRfdHVybjUgPC0gdHJhaW4oQmlqZWwgfi4sIGRhdGE9ZGF0SW1wcm92ZWRTbWFsbCwgbWV0aG9kPSJrbm4iLCB0ckNvbnRyb2w9dHJDdHJsLCB0dW5lTGVuZ3RoPTQyKQplcnJvcnNbMV09MS1rbm5fZml0X3R1cm41JHJlc3VsdHNbcm93Lm5hbWVzKGtubl9maXRfdHVybjUkYmVzdFR1bmUpLF0kQWNjdXJhY3kKI3Bsb3Qoa25uX2ZpdF90dXJuKQoKc2V0LnNlZWQoMTIzNCkKa25uX2ZpdF90dXJuNCA8LSB0cmFpbihCaWplbCB+LiwgZGF0YT1kYXRJbXByb3ZlZFNtYWxsW2MoIkJpamVsIiwgIkF1dG8uRmlyc3QuVHVybiIsICJSYWQuTGluZS5HcmFkaWVudHMiLCAiUmFkLlZhbC4zMCIsICJBdXRvLlBlYWsuRGVwdGgiKV0sIG1ldGhvZD0ia25uIiwgdHJDb250cm9sPXRyQ3RybCwgdHVuZUxlbmd0aD00MikKZXJyb3JzWzJdPTEta25uX2ZpdF90dXJuNCRyZXN1bHRzW3Jvdy5uYW1lcyhrbm5fZml0X3R1cm40JGJlc3RUdW5lKSxdJEFjY3VyYWN5CgpzZXQuc2VlZCgxMjM0KQprbm5fZml0X3R1cm4zIDwtIHRyYWluKEJpamVsIH4uLCBkYXRhPWRhdEltcHJvdmVkU21hbGxbYygiQmlqZWwiLCAiQXV0by5GaXJzdC5UdXJuIiwgIlJhZC5MaW5lLkdyYWRpZW50cyIsICJSYWQuVmFsLjMwIildLCBtZXRob2Q9ImtubiIsIHRyQ29udHJvbD10ckN0cmwsIHR1bmVMZW5ndGg9NDIpCmVycm9yc1szXT0xLWtubl9maXRfdHVybjMkcmVzdWx0c1tyb3cubmFtZXMoa25uX2ZpdF90dXJuMyRiZXN0VHVuZSksXSRBY2N1cmFjeQoKc2V0LnNlZWQoMTIzNCkKa25uX2ZpdF90dXJuMiA8LSB0cmFpbihCaWplbCB+LiwgZGF0YT1kYXRJbXByb3ZlZFNtYWxsW2MoIkJpamVsIiwgIkF1dG8uRmlyc3QuVHVybiIsICJSYWQuTGluZS5HcmFkaWVudHMiKV0sIG1ldGhvZD0ia25uIiwgdHJDb250cm9sPXRyQ3RybCwgdHVuZUxlbmd0aD00MikKZXJyb3JzWzRdPTEta25uX2ZpdF90dXJuMiRyZXN1bHRzW3Jvdy5uYW1lcyhrbm5fZml0X3R1cm4yJGJlc3RUdW5lKSxdJEFjY3VyYWN5CgpzZXQuc2VlZCgxMjM0KQprbm5fZml0X3R1cm4xIDwtIHRyYWluKEJpamVsIH4uLCBkYXRhPWRhdEltcHJvdmVkU21hbGxbYygiQmlqZWwiLCAiQXV0by5GaXJzdC5UdXJuIildLCBtZXRob2Q9ImtubiIsIHRyQ29udHJvbD10ckN0cmwsIHR1bmVMZW5ndGg9NDIpCmVycm9yc1s1XT0xLWtubl9maXRfdHVybjEkcmVzdWx0c1tyb3cubmFtZXMoa25uX2ZpdF90dXJuMSRiZXN0VHVuZSksXSRBY2N1cmFjeQoKCmBgYAoKUGxvdCB0aGUgcmVzdWx0cwoKYGBge3J9CmVycm9ycwpwbmcoZmlsZT0nL1VzZXJzL3MxMTAxMTUzL0Rlc2t0b3AvYmlqZWxzMV9ncmFwaHMvZXJyb3JfdnNfbnVtX3ZhcnMucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAKcGxvdChjKDUsNCwzLDIsMSksIGVycm9ycywgeGxhYj0iTnVtYmVyIG9mIHZhcmlhYmxlcyBpbiBtb2RlbCIsIHlsYWI9IkNsYXNzaWZpY2F0aW9uIGVycm9yIiwgcGNoPTE5LCB5bGltPWMoLjE1LCAuMikpCmRldi5vZmYoKQoKcHJlZDIgPC0gcHJlZGljdChrbm5fZml0X3R1cm4yLCBkYXRhPWRhdEltcHJvdmVkU21hbGxbYygiQmlqZWwiLCAiQXV0by5GaXJzdC5UdXJuIiwgIlJhZC5MaW5lLkdyYWRpZW50cyIpXSkKCnBuZyhmaWxlPScvVXNlcnMvczExMDExNTMvRGVza3RvcC9iaWplbHMxX2dyYXBocy9saXF1aWRfMnZhcnMucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAgCnBsb3QoQXV0by5GaXJzdC5UdXJuLCBSYWQuTGluZS5HcmFkaWVudHMsIGNvbD1CaWplbCwgcGNoPTE2KQpwb2ludHMoQXV0by5GaXJzdC5UdXJuLCBSYWQuTGluZS5HcmFkaWVudHMsIGNvbD1wcmVkMiwgcGNoPTEsIGNleD0xLjUpCmRldi5vZmYoKQoKCnByZWQxIDwtIHByZWRpY3Qoa25uX2ZpdF90dXJuMSwgZGF0YT1kYXRJbXByb3ZlZFNtYWxsW2MoIkJpamVsIiwgIkF1dG8uRmlyc3QuVHVybiIpXSkKc2V0LnNlZWQoMTIzNCkKeT1qaXR0ZXIocmVwKDAsIGVhY2g9MTM1KSkKcG5nKGZpbGU9Jy9Vc2Vycy9zMTEwMTE1My9EZXNrdG9wL2JpamVsczFfZ3JhcGhzL2xpcXVpZF9yZXN1bHQucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKSAgCnBsb3QoeX5BdXRvLkZpcnN0LlR1cm4sIGNvbD1CaWplbCwgcGNoPTE2LCB5YXh0PSduJywgeGxhYj0iUG9zaXRpb24gb2YgZmlyc3QgdHVybmluZyBwb2ludCBpbiBMaXF1aWQgQ2hhbm5lbCBBQ0YgKM68bSkiLCB5bGFiPSIiLCBsb2c9IngiKQpwb2ludHMoeX5BdXRvLkZpcnN0LlR1cm4sIGNvbD1wcmVkMSwgcGNoPTEsIGNleD0xLjUpCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQ9YygiQmlqZWwiLCAiTm9uLWJpamVsIiwgIlByZWQuIGJpamVsIiwgIlByZWQuIG5vbi1iaWplbCIpLCBwY2g9YygxNiwxNiwxLDEpLCBjb2w9YygicmVkIiwgImJsYWNrIiwgInJlZCIsICJibGFjayIpKQpkZXYub2ZmKCkKCgpgYGAKCgoKCiMgRmluYWwgbW9kZWwgCgpgYGB7cn0KZmluYWxfbW9kZWwgPC0gdHJhaW4oQmlqZWwgfi4sIGRhdGE9ZGF0SW1wcm92ZWRTbWFsbFtjKCJCaWplbCIsICJBdXRvLkZpcnN0LlR1cm4iKV0sIG1ldGhvZD0ia25uIiwgdHJDb250cm9sPXRyQ3RybCwgdHVuZUxlbmd0aD00MikKZXJyb3JzWzVdPTEta25uX2ZpdF90dXJuMSRyZXN1bHRzW3Jvdy5uYW1lcyhrbm5fZml0X3R1cm4xJGJlc3RUdW5lKSxdJEFjY3VyYWN5CnBuZyhmaWxlPScvVXNlcnMvczExMDExNTMvRGVza3RvcC9iaWplbHMxX2dyYXBocy9saXF1aWRfcmVzdWx0X2tfZ3JhcGgucG5nJywgd2lkdGg9NjAwLCBoZWlnaHQ9NDAwKQpwbG90KGZpbmFsX21vZGVsKQpkZXYub2ZmKCkKCnByZWRfZmluYWwgPC0gcHJlZGljdChmaW5hbF9tb2RlbCwgZGF0YT1kYXRJbXByb3ZlZFNtYWxsW2MoIkJpamVsIiwgIkF1dG8uRmlyc3QuVHVybiIpXSkKcHJlZF9maW5hbAoKdGFibGUoUHJlZGljdD1wcmVkX2ZpbmFsLCB0cnVlPUJpamVsKQoKYGBgCgoKIyBBcHBseWluZyB0aGUgZmluYWwgbW9kZWwgdG8gbmV3IGRhdGEKIyMgRmlyc3QgdHJ5IGJyYW5kIG5ldywgdW5yZWxhdGVkIGRhdGEKRmlyc3QgcmVhZCBpbiB0aGUgbmV3IGZpbGVzOgpgYGB7cn0KY29yckZpbGVzTmV3IDwtIGxpc3QuZmlsZXMoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvYWNmX25ldy8wIiwgcGF0dGVybj0iLnR4dCIsIGZ1bGwubmFtZXMgPSBUUlVFKQpjb3JyRmlsZU5hbWVzTmV3IDwtIGxpc3QuZmlsZXMoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvYWNmX25ldy8wIiwgcGF0dGVybj0iLnR4dCIpCgphdXRvQ29yck5ldyA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkoY29yckZpbGVzTmV3LCByZWFkLmNzdiwgaGVhZGVyPUZBTFNFKSkKY29sbmFtZXMoYXV0b0NvcnJOZXcpIDwtIGNvcnJGaWxlTmFtZXNOZXcKCmV4cF9EYXRhTmV3IDwtIHJlYWQuY3N2KCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL25ld19EYXRhL0JpamVsX0RhdGFfQmF0Y2gxLmNzdiIsIG5hLnN0cmluZ3MgPSAiPyIpCmV4cF9EYXRhTmV3JFNhbXBsZS5OdW1iZXIgPC0gYXMuY2hhcmFjdGVyKGV4cF9EYXRhTmV3JFNhbXBsZS5OdW1iZXIpCgpjb3JyRmlsZUlETmV3IDwtIHNhcHBseShzdHJzcGxpdChjb3JyRmlsZU5hbWVzTmV3LCJfIiksIGBbYCwxKSAjYFtgIGlzIGEgZnVuY3Rpb24gdGhhdCB0YWtlcyB0aGUgc3Vic2V0IG9mIHgsIHRoZSBpbnB1dCB0byB0aGlzIGZ1bmN0aW9uIGlzIHggKHN0cnNwbGl0Li4uKSBhbmQgdGhlIGVsZW1lbnQgb2YgeCB0aGF0IEkgd2FudCwgaWUgdGhlIDFzdCBvbmUKY29sbmFtZXMoYXV0b0NvcnJOZXcpIDwtIGNvcnJGaWxlSUROZXcKCnJvd25hbWVzKGV4cF9EYXRhTmV3KSA8LSBleHBfRGF0YU5ldyRTYW1wbGUuTnVtYmVyCmF1dG9Db3JyTmV3X3RyYW5zcG9zZSA8LSBkYXRhLmZyYW1lKHQoYXV0b0NvcnJOZXcpKQpleHBfRGF0YU5ldyRBdXRvY29ycmVsYXRpb24gPC0gYXV0b0NvcnJOZXdfdHJhbnNwb3NlW21hdGNoKHJvdy5uYW1lcyhleHBfRGF0YU5ldykscm93Lm5hbWVzKGF1dG9Db3JyTmV3X3RyYW5zcG9zZSkpLGMoMToyNTYpXQoKbnVtX3BvaW50cyA8LSBkaW0oZXhwX0RhdGFOZXcpWzFdCgp0dXJuaW5nUG9pbnRzTmV3IDwtIGxhcHBseSgxOm51bV9wb2ludHMsIGZ1bmN0aW9uKHkpIHR1cm5wb2ludHModW5saXN0KGV4cF9EYXRhTmV3JEF1dG9jb3JyZWxhdGlvblt5LF0pKSkKCgpleHBfRGF0YU5ldyRBdXRvLlR1cm5pbmcuUG9pbnRzIDwtIHR1cm5pbmdQb2ludHNOZXcKCmZpcnN0VHVybk5ldyA8LSBsYXBwbHkoMTpudW1fcG9pbnRzLCBmdW5jdGlvbih5KSBleHBfRGF0YU5ldyRBdXRvLlR1cm5pbmcuUG9pbnRzW1t5XV0kdHBwb3NbWzFdXSkKZXhwX0RhdGFOZXckQXV0by5GaXJzdC5UdXJuIDwtIHVubGlzdChmaXJzdFR1cm5OZXcpCgoKbmV3X2Zvcl9tb2RlbCA8LSBleHBfRGF0YU5ld1tjKDE2LDE5KV0KCmBgYAoKCk5vdyBhcHBseSB0aGUgZmluYWwgbW9kZWwgdG8gdGhlIG5ldyBkYXRhOgpgYGB7cn0KYmlqZWxfcHJlZCA9IHByZWRpY3QoZmluYWxfbW9kZWwsIG5ld2RhdGEgPSBuZXdfZm9yX21vZGVsKQpiaWplbF90cnVlID0gbmV3X2Zvcl9tb2RlbCRCaWplbAoKc3VjY2Vzc19jb3VudD1sZW5ndGgoYmlqZWxfcHJlZFtiaWplbF9wcmVkPT1iaWplbF90cnVlXSkKc3VjY2Vzc19yYXRlPXN1Y2Nlc3NfY291bnQvbGVuZ3RoKGJpamVsX3ByZWQpCnBhc3RlMCgiU3VjY2VzcyByYXRlOiAiLDEwMCpzdWNjZXNzX3JhdGUsIiUiKQoKbnVsbF9yYXRlID0gbGVuZ3RoKGJpamVsX3RydWVbYmlqZWxfdHJ1ZT09J3knXSkvbGVuZ3RoKGJpamVsX3ByZWQpCgoKcGFzdGUwKCJOdWxsIHJhdGU6ICIsIDEwMCpudWxsX3JhdGUsICIlIikKCnRhYmxlKFByZWRpY3Q9YmlqZWxfcHJlZCwgdHJ1ZT1iaWplbF90cnVlKQpwcmludChkYXRhLmZyYW1lKGJpamVsX3ByZWQsIGJpamVsX3RydWU9YmlqZWxfdHJ1ZSkpCmBgYAoKCiMjTm93IHJlcGVhdCB0aGF0IHdpdGggZGF0YSBtb3JlIHNpbWlsYXIgdG8gdGhlIGRhdGEgdGhlIG1vZGVsIHdhcyB0cmFpbmVkIG9uOgoKRmlyc3QgcmVhZCBpbiB0aGUgbmV3IGZpbGVzIChmcm9tIHNhbWVTYW1wbGVfYW5hbHlzaXMuUiBpbiBHaXQgZm9sZGVyKToKYGBge3J9CmJpamVsRmlsZXNMIDwtIGxpc3QuZmlsZXMoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvc2FtcGxlNTJpaS9saXF1aWQiLCBwYXR0ZXJuPSIudHh0IiwgZnVsbC5uYW1lcyA9IFRSVUUpCmJpamVsRmlsZU5hbWVzIDwtIGxpc3QuZmlsZXMoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvc2FtcGxlNTJpaS9saXF1aWQiLCBwYXR0ZXJuPSIudHh0IikKCgpmYWlsRmlsZXNMIDwtIGxpc3QuZmlsZXMoIi9Wb2x1bWVzL1BoRC9CaWplbERhdGEvc2FtcGxlNTRpL2xpcXVpZCIsIHBhdHRlcm49Ii50eHQiLCBmdWxsLm5hbWVzID0gVFJVRSkKZmFpbEZpbGVOYW1lcyA8LSBsaXN0LmZpbGVzKCIvVm9sdW1lcy9QaEQvQmlqZWxEYXRhL3NhbXBsZTU0aS9saXF1aWQiLCBwYXR0ZXJuPSIudHh0IikKCmF1dG9Db3JyQkwgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KGJpamVsRmlsZXNMLCByZWFkLmNzdiwgaGVhZGVyPUZBTFNFKSkKYmlqZWxJRCA8LSBzYXBwbHkoc3Ryc3BsaXQoYmlqZWxGaWxlTmFtZXMsIl8iKSwgYFtgLDEpCgoKYmlqZWxMYWJzIDwtIHJlcF9sZW4oInkiLCBsZW5ndGgoYmlqZWxGaWxlTmFtZXMpKQpiRGF0IDwtIGRhdGEuZnJhbWUoQmlqZWw9YmlqZWxMYWJzKQpyb3duYW1lcyhiRGF0KSA8LSBiaWplbElECmJEYXQkTGlxIDwtIGRhdGEuZnJhbWUodChhdXRvQ29yckJMKSkKCgoKYXV0b0NvcnJOQkwgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KGZhaWxGaWxlc0wsIHJlYWQuY3N2LCBoZWFkZXI9RkFMU0UpKQpmYWlsSUQgPC0gc2FwcGx5KHN0cnNwbGl0KGZhaWxGaWxlTmFtZXMsIl8iKSwgYFtgLDEpCiNjb2xuYW1lcyhhdXRvQ29yck5CTCkgPC0gY29sbmFtZXMoYXV0b0NvcnJOQlApIDwtIGZhaWxJRAoKZmFpbExhYnMgPC0gcmVwX2xlbigibiIsIGxlbmd0aChmYWlsRmlsZU5hbWVzKSkKbmJEYXQgPC0gZGF0YS5mcmFtZShCaWplbD1mYWlsTGFicykKbmJEYXQkTGlxIDwtIGRhdGEuZnJhbWUodChhdXRvQ29yck5CTCkpCgoKbFR1cm5zQiA8LSBsYXBwbHkoMTo4LCBmdW5jdGlvbihuKSB0dXJucG9pbnRzKHVubGlzdChiRGF0JExpcVtuLF0pKSkKZmlyc3RUdXJuQiA8LSBsYXBwbHkoMTo4LCBmdW5jdGlvbihtKSBsVHVybnNCW1ttXV0kdHBwb3NbMV0pCmJEYXQkQXV0by5GaXJzdC5UdXJuIDwtIHVubGlzdChmaXJzdFR1cm5CKQoKCmxUdXJuc05CIDwtIGxhcHBseSgxOjExLCBmdW5jdGlvbihuKSB0dXJucG9pbnRzKHVubGlzdChuYkRhdCRMaXFbbixdKSkpCmZpcnN0VHVybk5CIDwtIGxhcHBseSgxOjExLCBmdW5jdGlvbihtKSBsVHVybnNOQltbbV1dJHRwcG9zWzFdKQpuYkRhdCRBdXRvLkZpcnN0LlR1cm4gPC0gdW5saXN0KGZpcnN0VHVybk5CKQoKYXR0YWNoKGJEYXQpCnRlc3REYXQxIDwtIGRhdGEuZnJhbWUoQmlqZWwsIEF1dG8uRmlyc3QuVHVybikKCmF0dGFjaChuYkRhdCkKdGVzdERhdDIgPC0gZGF0YS5mcmFtZShCaWplbCwgQXV0by5GaXJzdC5UdXJuKQoKdGVzdERhdCA8LSByYmluZCh0ZXN0RGF0MSwgdGVzdERhdDIpCnRlc3REYXQKYGBgCgoKTm93IGFwcGx5IHRoZSBmaW5hbCBtb2RlbCB0byB0aGUgbmV3IGRhdGE6CmBgYHtyfQpiaWplbF9wcmVkID0gcHJlZGljdChmaW5hbF9tb2RlbCwgbmV3ZGF0YSA9IHRlc3REYXQpCmJpamVsX3RydWUgPSB0ZXN0RGF0JEJpamVsCgoKbGVuZ3RoKGJpamVsX3ByZWQpCmxlbmd0aChiaWplbF90cnVlKQoKc3VjY2Vzc19jb3VudD1sZW5ndGgoYmlqZWxfcHJlZFtiaWplbF9wcmVkPT1iaWplbF90cnVlXSkKc3VjY2Vzc19jb3VudApzdWNjZXNzX3JhdGU9c3VjY2Vzc19jb3VudC9sZW5ndGgoYmlqZWxfcHJlZCkKcGFzdGUwKCJTdWNjZXNzIHJhdGU6ICIsMTAwKnN1Y2Nlc3NfcmF0ZSwiJSIpCgpudWxsX3JhdGUgPSAxLShsZW5ndGgoYmlqZWxfdHJ1ZVtiaWplbF90cnVlPT0neSddKS9sZW5ndGgoYmlqZWxfcHJlZCkpCgoKcGFzdGUwKCJOdWxsIHJhdGU6ICIsIDEwMCpudWxsX3JhdGUsICIlIikKCnRhYmxlKFByZWRpY3Q9YmlqZWxfcHJlZCwgdHJ1ZT1iaWplbF90cnVlKQpwcmludChkYXRhLmZyYW1lKGJpamVsX3ByZWQsIGJpamVsX3RydWU9YmlqZWxfdHJ1ZSkpCmBgYAoK